
#include "Model.h"
#include "MTL.h"
#include "Texture.h"
using namespace std;

Vertex ** verticesData;
int * nVertex;

Model::Model(void)
{
	numberOfParts = 0;
}

void Model::LoadNFG(const char * path, Vertex ** &vData, int * &nVertex, GLuint ** &iData, int * &nIndex)
{
	numberOfParts = 1;
	nIndex = new int[numberOfParts];

	FILE * file = fopen(path, "r");
	if( file == NULL ){
		return;
	}

	myBox.xMin = 0;
	myBox.yMin = 0;
	myBox.zMin = 0;

	myBox.xMax = 0;
	myBox.yMax = 0;
	myBox.zMax = 0;

	// Load model
	int numVertex = 0;
	fscanf(file, "NrVertices: %d\n", &numVertex);

	Vertex *vertices = new Vertex[numVertex];
	const char *stringVertices = "%*d. pos:[%f, %f, %f]; norm:[%f, %f, %f]; binorm:[%*f, %*f, %*f]; tgt:[%*f, %*f, %*f]; uv:[%f, %f];\n";
	int i = 0;

	Vector3 pos;

	while(i < numVertex)
	{
		fscanf(file, stringVertices,
			&pos.x, &pos.y, &pos.z,
			&vertices[i].normal.x, &vertices[i].normal.y, &vertices[i].normal.z,
			//&vertices[i].binormal.x, &vertices[i].binormal.y, &vertices[i].binormal.z,
			//&vertices[i].tangent.x, &vertices[i].tangent.y, &vertices[i].tangent.z,
			&vertices[i].uv.x, &vertices[i].uv.y);

		vertices[i].position = pos;

		myBox.xMin = (pos.x < myBox.xMin)? pos.x : myBox.xMin;
		myBox.xMax = (pos.x > myBox.xMax)? pos.x : myBox.xMax;
		myBox.yMin = (pos.y < myBox.yMin)? pos.y : myBox.yMin;
		myBox.yMax = (pos.y > myBox.yMax)? pos.y : myBox.yMax;
		myBox.zMin = (pos.z < myBox.zMin)? pos.z : myBox.zMin;
		myBox.zMax = (pos.z > myBox.zMax)? pos.z : myBox.zMax;

		i++;
	}

	// Read Indices
	fscanf(file, "NrIndices: %d\n", &nIndex[0]);
	const char* stringIndices = "%*d.    %d,    %d,    %d\n";
	unsigned int *indicesData = new unsigned int[nIndex[0]];
	i = 0;

	while(i < nIndex[0])
	{
		fscanf(file, stringIndices, &indicesData[i], &indicesData[i + 1], &indicesData[i + 2]);
		i += 3;
	}

	
	vboId1 = new GLuint[numberOfParts];
	iboId1 = new GLuint[numberOfParts];

	glGenBuffers(1, &vboId1[0]);
	glBindBuffer(GL_ARRAY_BUFFER, vboId1[0]);
	glBufferData(GL_ARRAY_BUFFER, numVertex*sizeof(Vertex), vertices, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	SAFE_DEL_ARRAY(vertices);

	glGenBuffers(1, &iboId1[0]);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,  iboId1[0]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER,  nIndex[0]*sizeof(indicesData), indicesData, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);	
	SAFE_DEL_ARRAY(indicesData);
}



void Model::Load1(const char * filePath)
{
	if ( strstr(filePath, ".nfg") )
	{
		this->LoadNFG(filePath, verticesData, nVertex, indicesData, nIndex);
	}
	else if ( strstr(filePath, ".obj") )
	{
		this->LoadOBJ(filePath, verticesData, nVertex, indicesData, nIndex);
	}

	
}



void Model::LoadOBJ(const char * filePath, Vertex ** &vData, int * &nVertex, GLuint ** &iData, int * &nIndex)
{
	string mtlFileName(filePath);
	int fileNamePos = mtlFileName.find_last_of('/');
	int folderPos = mtlFileName.find_last_of('/', fileNamePos -1);
	mtlFileName.replace(folderPos + 1, fileNamePos - folderPos - 1, "mtl");
	mtlFileName.replace(mtlFileName.length() - 3, 3, "mtl");

	myBox.xMin = 0;
	myBox.yMin = 0;
	myBox.zMin = 0;

	myBox.xMax = 0;
	myBox.yMax = 0;
	myBox.zMax = 0;


	vector<string> materialNames;
	vector<string> texturePaths;
	vector<Vector4> color;
	loadMTL(mtlFileName.data(), materialNames, texturePaths, color);
	if ( materialNames.size() > 0 )
	{
		numberOfParts = materialNames.size();
	}
	else
	{
		numberOfParts = 1;
	}

	vData = new Vertex * [numberOfParts];
	nVertex = new int[numberOfParts];
	iData = new GLuint * [numberOfParts];
	nIndex = new int[numberOfParts];

	FILE * pFile = fopen(filePath, "r");

	if ( !pFile )
	{
		printf("FILE %s NOT FOUND!\n", filePath);
		return;
	}

	vector< Vector3 > tmpPosition;
	vector< Vector2 > tmpUv;
	vector< Vector3 > tmpNormal;
	vector< vector<GLuint> > positionIndices, uvIndices, normalIndices;


	int index = 0;
	for ( int i = 0; i < numberOfParts; i++ )
	{
		vector<GLuint> position, uv, normal;
		positionIndices.push_back(position);
		uvIndices.push_back(uv);
		normalIndices.push_back(normal);
	}

	while (!feof(pFile))
	{
		char lineHeader[128];
		fscanf(pFile, "\n%s", lineHeader);
		if ( strcmp(lineHeader, "usemtl") == 0 )
		{
			char mtlName[256];
			fscanf(pFile, "\n%s", mtlName);
			for (int i = 0; i < numberOfParts; i++)
			{
				if ( materialNames.size() > 0 )
				{
					if ( strcmp(materialNames[i].data(), mtlName) == 0 )
					{
						index = i;
						break;
					}
				}
			}
			if (index == -1)
			{
				index = 0;
			}
		}
		else if ( strcmp(lineHeader, "v") == 0 )
		{
			Vector3 position;
			fscanf(pFile, "\n%f %f %f", &position.x, &position.y, &position.z);
			tmpPosition.push_back(position);
		}
		else if ( strcmp(lineHeader, "vt") == 0 )
		{
			Vector2 uv;
			fscanf(pFile, "\n%f %f", &uv.x, &uv.y);
			tmpUv.push_back(uv);
		}
		else if ( strcmp(lineHeader, "vn") == 0 )
		{
			Vector3 normal;
			fscanf(pFile, "\n%f %f %f", &normal.x, &normal.y, &normal.z);
			tmpNormal.push_back(normal);
		}
		else if ( strcmp(lineHeader, "f") == 0 )
		{
			int positionIndex[3] = {0}, uvIndex[3] = {0}, normalIndex[3] = {0};
			char buffer[256];
			fgets(buffer, 256, pFile);
			int matches = sscanf(buffer, "%d/%d/%d %d/%d/%d %d/%d/%d",
				&positionIndex[0], &uvIndex[0], &normalIndex[0],
				&positionIndex[1], &uvIndex[1], &normalIndex[1],
				&positionIndex[2], &uvIndex[2], &normalIndex[2]);

				

		if (matches != 9)
			{
				matches = sscanf(buffer, "%d/%d %d/%d %d/%d",
					&positionIndex[0], &uvIndex[0],
					&positionIndex[1], &uvIndex[1],
					&positionIndex[2], &uvIndex[2]);
				if (matches != 6)
				{
					matches = sscanf(buffer, "%d//%d %d//%d %d//%d",
						&positionIndex[0], &normalIndex[0],
						&positionIndex[1], &normalIndex[1],
						&positionIndex[2], &normalIndex[2]);
				}
			}
			positionIndices[index].push_back(positionIndex[0]);
			positionIndices[index].push_back(positionIndex[1]);
			positionIndices[index].push_back(positionIndex[2]);

			uvIndices[index].push_back(uvIndex[0]);
			uvIndices[index].push_back(uvIndex[1]);
			uvIndices[index].push_back(uvIndex[2]);

			normalIndices[index].push_back(normalIndex[0]);
			normalIndices[index].push_back(normalIndex[1]);
			normalIndices[index].push_back(normalIndex[2]);
		}
		else
		{
			char tmp[1024];
			fgets(tmp, 1024, pFile);
		}
	}

	fclose(pFile);

	

	for ( int i = 0; i < numberOfParts; i++ )
	{
		nVertex[i] = nIndex[i] = positionIndices[i].size();
		vData[i] = new Vertex[nVertex[i]];
		iData[i] = new GLuint[nIndex[i]];
		for ( int j = 0; j < nVertex[i]; j++ )
		{
			if ( positionIndices[i].size() > 0 )
			{
				vData[i][j].position = tmpPosition[positionIndices[i][j] - 1];


				//vertices[i].position = pos;

				myBox.xMin = (vData[i][j].position.x < myBox.xMin)? vData[i][j].position.x : myBox.xMin;
				myBox.xMax = (vData[i][j].position.x > myBox.xMax)? vData[i][j].position.x : myBox.xMax;
				myBox.yMin = (vData[i][j].position.y < myBox.yMin)? vData[i][j].position.y : myBox.yMin;
				myBox.yMax = (vData[i][j].position.y > myBox.yMax)? vData[i][j].position.y : myBox.yMax;
				myBox.zMin = (vData[i][j].position.z < myBox.zMin)? vData[i][j].position.z : myBox.zMin;
				myBox.zMax = (vData[i][j].position.z > myBox.zMax)? vData[i][j].position.z : myBox.zMax;


			}
			if ( color.size() > 0 )
			{
				vData[i][j].color = color[i];
			}
			if ( uvIndices[i].size() > 0 )
			{
				vData[i][j].uv = tmpUv[uvIndices[i][j] - 1];
			}
			if ( normalIndices[i].size() > 0 )
			{
				vData[i][j].normal = tmpNormal[normalIndices[i][j] - 1];
			}
			iData[i][j] = j;
			
		}
	}

	vboId1 = new GLuint[numberOfParts];
	iboId1 = new GLuint[numberOfParts];

	for (int i = 0; i < numberOfParts; i++)
	{
		glGenBuffers(1, &this->vboId1[i]);
		glBindBuffer(GL_ARRAY_BUFFER, this->vboId1[i]);
		glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * nVertex[i], verticesData[i], GL_STATIC_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		SAFE_DEL_ARRAY(verticesData[i]);

		glGenBuffers(1, &this->iboId1[i]);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->iboId1[i]);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * nIndex[i], indicesData[i], GL_STATIC_DRAW);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
		SAFE_DEL_ARRAY(indicesData[i]);
		
	}
	
}


MyBox Model::GetBox()
{
	return myBox;
}

Model::~Model(void)
{
}

int Model::GetNumberOfParts()
{
	return numberOfParts;
}

GLuint * Model::GetVboId1()
{
	return this->vboId1;
}

GLuint * Model::GetIboId1()
{
	return this->iboId1;
}

int * Model::GetIndexSize()
{
	return this->nIndex;
}

char* Model::GetId(void)
{
      return this->id; 
}
void Model::SetId(const char *ID)
{
	this->id = new char[strlen(ID)+1];
	strcpy(this->id, ID);
}




void Model::Destroy()
{
	for (register int i = 0; i < numberOfParts; i++)
	{
		if (vboId1[i] != 0)
		{
			glDeleteBuffers(1, &vboId1[i]);
			vboId1[i] = 0;
		}

		if (iboId1[i] != 0)
		{
			glDeleteBuffers(1, &iboId1[i]);
			iboId1[i] = 0;
		}

		if (indicesData != 0)
		{
			SAFE_DEL_ARRAY(indicesData[i]);
		}
	}
	SAFE_DEL_ARRAY(vboId1);
	SAFE_DEL_ARRAY(iboId1);
	if (indicesData != 0)
	{
			SAFE_DEL_ARRAY(indicesData);
	}
	SAFE_DEL_ARRAY(nIndex);
}